//
// Created by hjie on 23-5-15.
//

#include "data_buffer.h"
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include <vector>

/**
 * buffer_begin_index       data_begin_index                data_end_index                       buffer_end_index
 *     |--------------------------|10101010010101010101001010101010|------------------------------------|
 */

DataBuffer::DataBuffer(int socket)
{
    m_fd = socket;

    m_buffer_begin_index = 0;
    m_buffer_end_index   = 0;

    m_data_current_index = 0;
    m_data_end_index     = 0;

    m_data.resize(BUFFER_SIZE);
    
    m_buffer_end_index  = m_data.size();
}

DataBuffer::~DataBuffer()
{
    m_fd = 0;
    m_data.clear();
    m_data.shrink_to_fit();
}

uint8_t *DataBuffer::Data()
{
    return &(*(m_data.begin() + m_data_current_index));
}

int DataBuffer::Length()
{
    return (m_data_end_index - m_data_current_index);
}

int DataBuffer::FreeSize()
{
    return (m_buffer_end_index - m_data_end_index);
}

void DataBuffer::RemoveData(int length)
{
    m_data_current_index += length;
    if (m_data_current_index == m_data_end_index)
    {
        m_data_current_index = m_buffer_begin_index;
        m_data_end_index     = m_buffer_begin_index;   
    }
}

void DataBuffer::PushDataBuffer(uint8_t *data, int length)
{
    int buffer_free_size        = (m_buffer_end_index   - m_data_end_index);
    int data_size               = (m_data_end_index     - m_data_current_index);
    if (buffer_free_size >= length)
    {
        memcpy(&(*(m_data.begin() + m_data_end_index)), data, length);
        m_data_end_index += length;
    }
    else
    {
        if (data_size > 0)
        {
            memcpy(&(*m_data.begin()), &(*(m_data.begin() + m_data_current_index)), data_size);
            m_data_current_index = m_buffer_begin_index;
            m_data_end_index     = data_size;
        }
        buffer_free_size     = (m_buffer_end_index - m_data_end_index);
        if (buffer_free_size >= length)
        {
            memcpy(&(*(m_data.begin() + m_data_end_index)), data, length);
            m_data_end_index += length;
        }
        else
        {
            int size = m_data.size();
            m_data.resize(size + length + 1024);
            m_buffer_end_index = m_data.size();

            memcpy(&(*(m_data.begin() + m_data_end_index)), data, length);
            m_data_end_index += length;
        }
    }
}

int DataBuffer::WriteData(const uint8_t *data, int length)
{
    int write_size = write(m_fd, data, length);
    return write_size;
}

int DataBuffer::ReadData(int & status)
{

    char buffer[BUFFER_SIZE + 1] = {0};
    memset(buffer, 0, BUFFER_SIZE + 1);
    status = 0;
    int read_size_sum = 0;

    do 
    {
        std::memset(buffer, 0, BUFFER_SIZE + 1);
        size_t read_size = recv(m_fd, buffer, BUFFER_SIZE, 0);
        if (read_size <= 0)
        {
            if (read_size == 0)
            {
                status = -1;
            }
            else if (errno == ETIMEDOUT || errno == EAGAIN)
            {
                status = 0;
            }
            else 
            {
                status = -1;
            }
        }
        else
        {
            size_t buffer_free_size        = (m_buffer_end_index   - m_data_end_index);
            size_t data_size               = (m_data_end_index     - m_data_current_index);
            if (buffer_free_size >= read_size)
            {
                memcpy(&(*(m_data.begin() + m_data_end_index)), buffer, read_size);
                m_data_end_index += read_size;
            }   
            else
            {
                if (data_size > 0)
                {
                    memcpy(&(*m_data.begin()), &(*(m_data.begin() + m_data_current_index)), data_size);
                    m_data_current_index = m_buffer_begin_index;
                    m_data_end_index     = data_size;
                }
                buffer_free_size     = (m_buffer_end_index - m_data_end_index);
                if (buffer_free_size >= read_size)
                {
                    memcpy(&(*(m_data.begin() + m_data_end_index)), buffer, read_size);
                    m_data_end_index += read_size; 
                }
                else
                {
                    int size = m_data.size();
                    std::vector<uint8_t> data_buffer(size + read_size + 500);
                    memcpy(&(*(m_data.begin())), &(*m_data.begin()), data_size);
                    m_data_end_index = data_size;
                    m_data_current_index = 0;
                    memcpy(&(*(m_data.begin())), buffer, read_size);
                    m_data_end_index += read_size;
                    m_buffer_end_index = size + read_size + 500;

                    m_data.clear();
                    m_data.swap(data_buffer);
                }
            }
            if (read_size < BUFFER_SIZE)
            {
                status = 0;
            }
            else
            {
                status = 1;
            }
            read_size_sum += read_size;
        }

    } while(status > 0);

    if (status == -1)
    {
        // close or error
    }
    return read_size_sum;
}

int DataBuffer::WriteData(const uint8_t *data, int length, struct sockaddr *remote, socklen_t len)
{
    return sendto(m_fd, data, length, 0, remote, len);
}

int DataBuffer::ReadData(struct sockaddr *remote_addr, socklen_t *remote_len, int &status)
{
    int read_size_sum = 0;
    status = 0;
    char buffer[BUFFER_SIZE + 1] = {0};
    do
    {
        memset(buffer, 0, BUFFER_SIZE + 1);
        int read_size = recvfrom(m_fd, buffer, BUFFER_SIZE, 0, remote_addr, remote_len);
        if (read_size < 0)
        {
            if (errno == ETIMEDOUT || errno == EAGAIN || errno == EWOULDBLOCK)
            {
                status = 0;
            }
            else
            {
                status = -1;
            }
        }
        else
        {
            int buffer_free_size        = (m_buffer_end_index   - m_data_end_index);
            int data_size               = (m_data_end_index     - m_data_current_index);
            if (buffer_free_size >= read_size)
            {
                memcpy(&(*(m_data.begin() + m_data_end_index)), buffer, read_size);
                m_data_end_index += read_size;
            }
            else
            {
                if (data_size > 0)
                {
                    memcpy(&(*m_data.begin()), &(*(m_data.begin() + m_data_current_index)), data_size);
                    m_data_current_index = m_buffer_begin_index;
                    m_data_end_index     = data_size;
                }
                buffer_free_size     = (m_buffer_end_index - m_data_end_index);
                if (buffer_free_size >= read_size)
                {
                    memcpy(&(*(m_data.begin() + m_data_end_index)), buffer, read_size);
                    m_data_end_index += read_size;
                }
                else
                {
                    int size = m_data.size();
                    m_data.resize(size + read_size + 1024);
                    m_buffer_end_index = m_data.size();

                    memcpy(&(*(m_data.begin() + m_data_end_index)), buffer, read_size);
                    m_data_end_index += read_size;
                }
            }
            if (read_size < BUFFER_SIZE)
            {
                status = 0;
            }
            else
            {
                status = 1;
            }
            read_size_sum += read_size;
        }

    } while (status > 0);

    return read_size_sum;
}

